/** 
    @file input_manager.h
    @brief Input Manager Class
*/

#ifndef PM3_INPUT_MANAGER_H
#define PM3_INPUT_MANAGER_H

#include <OISMouse.h>
#include <OISKeyboard.h>
#include <OISJoyStick.h>
#include <OISInputManager.h>

#include <OgreRenderWindow.h>

namespace pm3 {

    /** 
        @class INPUT_MANAGER
        @brief The Input Manager Class
    */
    class INPUT_MANAGER : public OIS::KeyListener, public OIS::MouseListener, public OIS::JoyStickListener {
    public:
        /**
            @brief Overridden destructor
        */
        virtual ~INPUT_MANAGER( void );

        /**
            @brief Initialises OIS
            @param[in] p_render_window Pointer to the Ogre RenderWindow for mouse range
        */
        void initialise( Ogre::RenderWindow *p_render_window );

        /**
            @brief Clean OIS
        */
        void cleanup();

        /**
            @brief Starts capturing events
        */
        void capture( void );

        /**
            @brief Adds a keyboard listener
            @param[in] p_key_listener Pointer to the keyboard listener to be added
            @param[in] s_instance_name Name for the listener
        */
        void add_key_listener( OIS::KeyListener *p_key_listener, const std::string &s_instance_name );
        /**
            @brief Adds a mouse listener
            @param[in] p_mouse_listener Pointer to the mouse listener to be added
            @param[in] s_instance_name Name for the listener
        */
        void add_mouse_listener( OIS::MouseListener *p_mouse_listener, const std::string &s_instance_name );
        /**
            @brief Adds a joystick listener
            @param[in] p_joystick_listener Pointer to the joystick listener to be added
            @param[in] s_instance_name Name for the listener
        */
        void add_joystick_listener( OIS::JoyStickListener *p_joystick_listener, const std::string &s_instance_name );

        /**
            @brief Removes a keyboard listener by name
            @param[in] s_instance_name Name of the listener to be removed
        */
        void remove_key_listener( const std::string &s_instance_name );
        /**
            @brief Removes a mouse listener by name
            @param[in] s_instance_name Name of the listener to be removed
        */
        void remove_mouse_listener( const std::string &s_instance_name );
        /**
            @brief Removes a joystick listener by name
            @param[in] s_instance_name Name of the listener to be removed
        */
        void remove_joystick_listener( const std::string &s_instance_name );
        /**
            @brief Removes a keyboard listener by pointer
            @param[in] p_keyboard_listener Pointer to the listener to be removed
        */
        void remove_key_listener( OIS::KeyListener *p_key_listener );
        /**
            @brief Removes a mouse listener by pointer
            @param[in] p_mouse_listener Pointer to the listener to be removed
        */
        void remove_mouse_listener( OIS::MouseListener *p_mouse_listener );
        /**
            @brief Removes a joystick listener by pointer
            @param[in] p_joystick_listener Pointer to the listener to be removed
        */
        void remove_joystick_listener( OIS::JoyStickListener *p_joystick_listener );

        /**
            @brief Removes all listeners
        */
        void remove_all_listeners( void );
        /**
            @brief Removes all keyboard listeners
        */
        void remove_all_key_listeners( void );
        /**
            @brief Removes all mouse listeners
        */
        void remove_all_mouse_listeners( void );
        /**
            @brief Removes all joystick listeners
        */
        void remove_all_joystick_listeners( void );

        /**
            @brief Sets window dimensions
            @param[int] width Width of the window
            @param[int] height Height of the window
        */
        void set_window_extents( int width, int height );

        /**
            @brief Returns a pointer to the mouse
            @return Mouse pointer
        */
        OIS::Mouse    *get_mouse( void );
        /**
            @brief Returns a pointer to the keyboard
            @return Keyboard pointer
        */
        OIS::Keyboard *get_keyboard( void );
        /**
            @brief Returns a pointer to the joystick
            @return Joystick pointer
        */
        OIS::JoyStick *get_joystick( unsigned int i_index );
        /**
            @brief Returns the number of joysticks
            @return Number of joysticks
        */
        int get_num_joysticks( void );

        /**
            @brief Returns a pointer to the input manager singleton
            @return Singleton pointer
        */
        static INPUT_MANAGER *get_singleton_ptr( void );

        /**
            @brief Limits mouse range to window size
        */
        inline void enable_mouse_limits() {
            m_limit_mouse = true;
        }
        /**
            @brief Allows mouse out of the window
        */
        inline void disable_mouse_limits() {
            m_limit_mouse = false;
        }

    private:
        /**
            @brief Singleton private constructor
        */
        INPUT_MANAGER( void );
        /**
            @brief Singleton private copy-constructor
            @param[in] r_mgr Reference to the instance to be copied
        */
        INPUT_MANAGER( const INPUT_MANAGER &r_mgr ) { }
        /**
            @brief Singleton private assignment operator
            @param[in] r_mgr Reference to the instance to be assigned to the current class
        */
        INPUT_MANAGER & operator = ( const INPUT_MANAGER &r_mgr );

        /**
            @brief OIS KeyPressed event listener
            @param[in] r_event Reference to the keyboard event that was captured
            @return True if the key can be removed from the buffer and false otherwise
        */
        bool keyPressed( const OIS::KeyEvent &r_event );
        /**
            @brief OIS KeyReleased event listener
            @param[in] r_event Reference to the keyboard event that was captured
            @return True if the key can be removed from the buffer and false otherwise
        */
        bool keyReleased( const OIS::KeyEvent &r_event );

        /**
            @brief MouseMove event listener
            @param[in] r_event Reference to the mouse event that was captured
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool mouseMoved( const OIS::MouseEvent &r_event );
        /**
            @brief MousePressed event listener
            @param[in] r_event Reference to the mouse event that was captured
            @param[in] i_id Index of the button that was pressed
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool mousePressed( const OIS::MouseEvent &r_event, OIS::MouseButtonID i_id );
        /**
            @brief MouseReleased event listener
            @param[in] r_event Reference to the mouse event that was captured
            @param[in] i_id Index of the button that was pressed
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool mouseReleased( const OIS::MouseEvent &r_event, OIS::MouseButtonID i_id );

        /**
            @brief Joystick PovMoved event listener
            @param[in] r_event Reference to the joystick event that was captured
            @param[in] i_pov The new position of the pov
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool povMoved( const OIS::JoyStickEvent &r_event, int i_pov );
        /**
            @brief Joystick AxisMoved event listener
            @param[in] r_event Reference to the joystick event that was captured
            @param[in] i_axis The new position of the axis
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool axisMoved( const OIS::JoyStickEvent &r_event, int i_axis );
        /**
            @brief Joystick SliderMoved event listener
            @param[in] r_event Reference to the joystick event that was captured
            @param[in] i_slider_id The new position id of the slider
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool sliderMoved( const OIS::JoyStickEvent &r_event, int i_slider_id );
        /**
            @brief Joystick ButtonPressed event listener
            @param[in] r_event Reference to the joystick event that was captured
            @param[in] i_button Index of the button that was pressed down
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool buttonPressed( const OIS::JoyStickEvent &r_event, int i_button );
        /**
            @brief Joystick ButtonReleased event listener
            @param[in] r_event Reference to the joystick event that was captured
            @param[in] i_button Index of the button that was released down
            @return True if the event can be removed from the buffer and false otherwise
        */
        bool buttonReleased( const OIS::JoyStickEvent &r_event, int i_button );

        //! Pointer to the mouse
        OIS::Mouse        *m_mouse;
        //! Pointer to the keyboard
        OIS::Keyboard     *m_keyboard;
        //! Pointer to the OIS input system
        OIS::InputManager *m_input_system;

        //! Array of joysticks
        std::vector<OIS::JoyStick*> m_joysticks;
        //! Current joystick iterator
        std::vector<OIS::JoyStick*>::iterator it_joystick;
        //! End of joysticks iterator
        std::vector<OIS::JoyStick*>::iterator it_joystick_end;

        //! Key listener name map
        std::map<std::string, OIS::KeyListener*> m_key_listeners;
        //! Mouse listener name map
        std::map<std::string, OIS::MouseListener*> m_mouse_listeners;
        //! Joystick listener name map
        std::map<std::string, OIS::JoyStickListener*> m_joystick_listeners;

        //! Keyboard listener iterator
        std::map<std::string, OIS::KeyListener*>::iterator it_key_listener;
        //! Mouse listener iterator
        std::map<std::string, OIS::MouseListener*>::iterator it_mouse_listener;
        //! Joystick listener iterator
        std::map<std::string, OIS::JoyStickListener*>::iterator it_joystick_listener;

        //! End of keyboard listeners iterator
        std::map<std::string, OIS::KeyListener*>::iterator it_key_listener_end;
        //! End of mouse listeners iterator
        std::map<std::string, OIS::MouseListener*>::iterator it_mouse_listener_end;
        //! End of joystick listeners iterator
        std::map<std::string, OIS::JoyStickListener*>::iterator it_joystick_listener_end;

        //! Should mouse range be limited?
        bool m_limit_mouse;

        //! Static pointer to the only instance of the input manager
        static INPUT_MANAGER *m_input_manager;
    };

}

#endif

